package cn.cocho.android.mc.file;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.Adler32;
import java.util.zip.CheckedInputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

import cn.cocho.android.mc.log.LoggerUtils;
import cn.cocho.android.mc.util.CommonContexts;

/**
 * 文件的压缩和解压（主要针对zip格式或与其等同的jar文件、apk文件等）
 * 
 * @author HAIKANG SONG
 * @time 2013-4-19上午9:55:44
 */
public class FileCompress {

	
	/**
	 * 将指定文件压缩为zip包、jar包或apk包等
	 * 
	 * @param sourcePath
	 *            被压缩的文件
	 * @param saveFilePath
	 *            压缩后文件的存放路径，如果为null,在存放在与被压缩文件同级同名的zip包中
	 * @author HAIKANG SONG
	 * @time 2013-4-19上午11:09:01
	 */
	public static void compress(String sourcePath, String saveFilePath) {
		File file = new File(sourcePath);
		List<File> allFiles = searchFiles(file.getPath(), new ArrayList<File>());
		Object[] fileArray = allFiles.toArray();
		BufferedInputStream bin = null;
		FileInputStream fin = null;
		ZipOutputStream zout = null;
		FileOutputStream fout = null;
		try {
			if (saveFilePath == null) {
				saveFilePath = file.getParent() + File.separator + file.getName() + ".zip";
			}
			FileUtils.createFile(saveFilePath);
			fout = new FileOutputStream(saveFilePath);
			zout = new ZipOutputStream(new BufferedOutputStream(fout, CommonContexts.fileBuffer));
			zout.setLevel(9);
			byte[] buffer = new byte[CommonContexts.fileBuffer];
			for (int i = 0; i < fileArray.length; i++) {
				File tempFile = new File(fileArray[i].toString());// 设置压缩文件入口entry,为被读取的文件创建压缩条目
				String rootStr = file.getPath();
				String entryStr = null;
				/*
				 * entry以相对路径的形式设置。以文件夹C:\temp例如temp\test.doc或者test.xls
				 * 如果设置不当，会出现拒绝访问等错误分别处理单个文件/目录的entry
				 */
				if (rootStr.equals(tempFile.getPath())) {
					entryStr = tempFile.getName();
				} else {
					entryStr = tempFile.getPath().substring((rootStr + File.separator).length());
				}
				ZipEntry entry = new ZipEntry(entryStr);
				zout.putNextEntry(entry);
				fin = new FileInputStream(tempFile);
				bin = new BufferedInputStream(fin, CommonContexts.fileBuffer);
				int count = 0;
				while ((count = bin.read(buffer, 0, CommonContexts.fileBuffer)) != -1) {
					zout.write(buffer, 0, count);
				}
			}
		} catch (Exception e) {
			LoggerUtils.error(FileCompress.class.getName(), "将[" + sourcePath + "]压缩到[" + saveFilePath + "]时出错", e);
		} finally {
			try {
				bin.close();
				fin.close();
				zout.finish();
				zout.close();
				fout.flush();
				fout.close();
			} catch (Exception e) {
			}
		}
	}

	/**
	 * 解压文件，支持与zip包同等格式的文件，如jar,apk
	 *
	 * @param filePath 被解压的文件
	 * @param savePath 解压后保存路径，如果为null则保存在与被解压文件同级同名的目录中
	 * @author HAIKANG SONG
	 * @time 2013-4-19上午11:37:35
	 */
	public static void decompress(String filePath, String savePath) {
		File file = new File(filePath);
		if(!file.exists()){
			LoggerUtils.warn(FileCompress.class.getName(), "将文件[" + filePath + "]解压到[" + savePath + "]时,被解压的文件不存在！");
			return;
		}
		BufferedOutputStream bout = null;
		FileInputStream fin = null;
		CheckedInputStream cin = null;
		ZipInputStream zin = null;
		if(savePath == null){
			int index = filePath.indexOf(".");
			savePath = filePath.substring(0, index)+File.separator;
		}
		FileUtils.mkdirs(savePath);
		try {
			fin = new FileInputStream(file);
			cin = new CheckedInputStream(fin, new Adler32());
			zin = new ZipInputStream(new BufferedInputStream(cin));
			ZipEntry entry = null;
			while ((entry = zin.getNextEntry()) != null) {
				int count;
				byte[] data = new byte[CommonContexts.fileBuffer];
				FileOutputStream fos = new FileOutputStream(savePath + newDir(savePath, entry.getName()));
				bout = new BufferedOutputStream(fos, CommonContexts.fileBuffer);
				while ((count = zin.read(data, 0, CommonContexts.fileBuffer)) != -1) {
					bout.write(data, 0, count);
				}
				bout.flush();
				bout.close();
			}
		} catch (Exception e) {
			LoggerUtils.error(FileCompress.class.getName(), "将文件[" + filePath + "]解压到[" + savePath + "]时出错", e);
		}finally{
			try {
				fin.close();
				cin.close();
				zin.close();
				bout.flush();
				bout.close();
			} catch (IOException e) {}
		}
	}

	private static List<File> searchFiles(String sourceFilePath, List<File> fileList) {
		File fileDir = new File(sourceFilePath);
		if (fileDir.isDirectory()) {
			File[] subfiles = fileDir.listFiles();
			for (int i = 0; i < subfiles.length; i++) {
				searchFiles(subfiles[i].getPath(), fileList);
			}
		} else {
			fileList.add(fileDir);
		}
		return fileList;
	}

	private static String newDir(String savePath, String entryName) {
		int index = entryName.lastIndexOf(File.separator);
		if (index != -1) {//如果路径为目录
			String path = entryName.substring(0, index);
			new File(savePath + File.separator + path).mkdirs();
		}
		return entryName;
	}
}
